package http_client

import (
	"bytes"
	"context"
	"encoding/json"
	"io"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// HttpClient HTTP客户端结构体
type HttpClient struct {
	client    *http.Client
	baseURL   string
	headers   map[string]string
	retry     int
	cookieJar *cookiejar.Jar
}

// Response 封装响应结果
type Response struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
}

// NewHttpClient 创建新的HTTP客户端
func NewHttpClient(timeout time.Duration, baseURL string, retry int) (*HttpClient, error) {
	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	return &HttpClient{
		client: &http.Client{
			Timeout: timeout,
			Jar:     jar,
		},
		baseURL:   baseURL,
		headers:   make(map[string]string),
		retry:     retry,
		cookieJar: jar,
	}, nil
}

// SendRequest 发送HTTP请求
func (c *HttpClient) SendRequest(method, path string, body io.Reader, headers map[string]string) (*Response, error) {
	// 合并基础URL和路径
	fullURL, err := url.JoinPath(c.baseURL, path)
	if err != nil {
		return nil, err
	}

	var resp *http.Response
	var req *http.Request
	var attempt int

	for attempt = 0; attempt <= c.retry; attempt++ {
		req, err = http.NewRequest(method, fullURL, body)
		if err != nil {
			continue
		}

		// 设置公共头部
		for k, v := range c.headers {
			req.Header.Set(k, v)
		}

		// 设置请求头部
		for k, v := range headers {
			req.Header.Set(k, v)
		}

		resp, err = c.client.Do(req)
		if err == nil && resp.StatusCode < 500 {
			break
		}
		if resp != nil {
			resp.Body.Close()
		}
	}

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	responseBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return &Response{
		StatusCode: resp.StatusCode,
		Headers:    resp.Header.Clone(),
		Body:       responseBody,
	}, nil
}

// Get 发送GET请求
func (c *HttpClient) Get(url string, headers map[string]string) (*Response, error) {
	return c.SendRequest(http.MethodGet, url, nil, headers)
}

// PostJSON 发送JSON格式的POST请求
func (c *HttpClient) PostJSON(url string, data interface{}, headers map[string]string) (*Response, error) {
	jsonData, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}

	if headers == nil {
		headers = make(map[string]string)
	}
	headers["Content-Type"] = "application/json"

	return c.SendRequest(http.MethodPost, url, bytes.NewReader(jsonData), headers)
}

// PostForm 发送表单POST请求
func (c *HttpClient) PostForm(url string, data url.Values, headers map[string]string) (*Response, error) {
	if headers == nil {
		headers = make(map[string]string)
	}
	headers["Content-Type"] = "application/x-www-form-urlencoded"

	return c.SendRequest(http.MethodPost, url, strings.NewReader(data.Encode()), headers)
}

// UploadFile 上传文件
func (c *HttpClient) UploadFile(url, fieldName, filePath string, extraFields map[string]string) (*Response, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 添加文件
	part, err := writer.CreateFormFile(fieldName, filepath.Base(file.Name()))
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		return nil, err
	}

	// 添加额外字段
	for k, v := range extraFields {
		_ = writer.WriteField(k, v)
	}

	err = writer.Close()
	if err != nil {
		return nil, err
	}

	headers := map[string]string{
		"Content-Type": writer.FormDataContentType(),
	}

	return c.SendRequest(http.MethodPost, url, body, headers)
}

// SetCookie 设置Cookie
func (c *HttpClient) SetCookie(u *url.URL, cookies []*http.Cookie) {
	c.cookieJar.SetCookies(u, cookies)
}

// GetCookies 获取指定URL的Cookie
func (c *HttpClient) GetCookies(u *url.URL) []*http.Cookie {
	return c.cookieJar.Cookies(u)
}

// WithBaseURL 链式调用设置基础URL
func (c *HttpClient) WithBaseURL(baseURL string) *HttpClient {
	c.baseURL = baseURL
	return c
}

// WithHeader 链式调用添加公共Header
func (c *HttpClient) WithHeader(key, value string) *HttpClient {
	c.headers[key] = value
	return c
}

// SetTimeout 设置超时时间
func (c *HttpClient) SetTimeout(timeout time.Duration) {
	c.client.Timeout = timeout
}

// SetProxy 设置代理
func (c *HttpClient) SetProxy(proxyURL string) error {
	proxy, err := url.Parse(proxyURL)
	if err != nil {
		return err
	}
	c.client.Transport = &http.Transport{
		Proxy: http.ProxyURL(proxy),
	}
	return nil
}

// WithContext 创建带有上下文的请求
func (c *HttpClient) WithContext(ctx context.Context, method, url string, body io.Reader) (*http.Request, error) {
	return http.NewRequestWithContext(ctx, method, url, body)
}
